<?php


namespace App\Service;


use EchoPHP\Facades\File;
use RuntimeException;
use InvalidArgumentException;

class DictGenerate extends Dict {

    /**
     * 静态字典资源文件原始路径
     * @var
     */
    protected $dictPath;
    protected $iniPath;
    protected $jsonPath;

    protected $delimiter = ' - ';

    public $debug = false;

    /**
     * 直辖市及深圳等其他特殊城市code，这些code与省一级code平齐
     * @var array
     */
    protected $municipality = ['010000', '020000', '040000', '050000', '060000'];
    /**
     * 证书列表排序
     * @var array
     */
    protected $certListSort = ['0100', '0400', '0500', '0601', '0700', '0200', '0300', '0800', '0900', '1000', '9900'];
    /**
     * 人工聚合排序
     * @var array
     */
    //json数据来源： https://js.51jobcdn.com/in/js/2016/layer/layer_main_navigation.js
    protected $categoryListSort = [
        DictConstant::DD_INDUSTRY => '[
{"c":"计算机/互联网/通信/电子","e":"Computer, Internet, Telecom, Electronics","nav":"01","category":["01","37","38","31","39","32","40","02","35"]},
{"c":"会计/金融/银行/保险","e":"Accounting, Finance, Banking, Insurance","nav":"41","category":["41","03","42","43","62"]},
{"c":"贸易/消费/制造/营运","e":"Trade,Sales, Manufacturing, Operations","nav":"04","category":["04","22","05","06","44","60","45","14","33","65"]},
{"c":"制药/医疗","e":"Pharmaceuticals, Healthcare","nav":"08","category":["08","46","47"]},
{"c":"广告/媒体","e":"Advertising, Media Related","nav":"12","category":["12","48","49","13","15"]},
{"c":"房地产/建筑","e":"Real Estates Related","nav":"26","category":["26","09","50","51","34","63"]},
{"c":"专业服务/教育/培训","e":"Professional Services, Education, Training","nav":"07","category":["07","59","52","18","23","24"]},
{"c":"服务业","e":"Customer Services","nav":"11","category":["11","53","17","54","27"]},
{"c":"物流/运输","e":"Logistics, Transportation","nav":"21","category":["21","55"]},
{"c":"能源/环保/化工","e":"Utilities, Environmental Protection, Chemical","nav":"19","category":["19","16","36","61","56","20"]},
{"c":"政府/非营利组织/其他","e":"Government, Non Profit, Others","nav":"28","category":["28","57","29","58"]}
]',
        DictConstant::DD_FUNCTYPE => '[
{"c":"计算机/互联网/通信/电子","e":"Computer,Internet,Telecom,Electronics","nav":"0100","category":["0100","7700","7200","7300","7800","7400","2700","7900","7500","6600","8000","2600","6100","6700","2900","2800"]},
{"c":"销售/客服/技术支持","e":"Sales,Customer Service,Technical Support","nav":"0200","category":["0200","3000","3100","3200"]},
{"c":"会计/金融/银行/保险","e":"Accounting,Finance,Banking,Insurance","nav":"0400","category":["0400","3300","2200","3400"]},
{"c":"生产/营运/采购/物流","e":"Manufacturing,Operation,Purchasing,Logistics","nav":"3500","category":["3500","3600","0500","6200","6300","7100","5400","5900","3700","3800","0800","3900","4000"]},
{"c":"生物/制药/医疗/护理","e":"Biotechnology,Pharmaceuticals,Healthcare","nav":"4100","category":["4100","5500","1300"]},
{"c":"广告/市场/媒体/艺术","e":"Advertising,Marketing,Media,Design","nav":"4200","category":["4200","4300","0300","4400","4500","0900"]},
{"c":"建筑/房地产","e":"Construction,Real Estate","nav":"6900","category":["6900","2100","4600","6000","4700"]},
{"c":"人事/行政/高级管理","e":"HR,Admin.,Senior Management","nav":"0600","category":["0600","2300","0700"]},
{"c":"咨询/法律/教育/科研","e":"Consultant,Legal,Education","nav":"1400","category":["1400","1100","1200","5700","1000"]},
{"c":"服务业","e":"Service","nav":"4800","category":["4800","4900","5000","5100","1800","5200","6400","6500"]},
{"c":"公务员/翻译/其他","e":"Official,Translator,Others","nav":"1500","category":["1500","2000","5600","5800","1600","1700","5300","1900"]}
]',
        DictConstant::DD_MAJOR => '[
{"c":"哲学","e":"Philosophy","nav":"1100","category":["1100"]},
{"c":"经济学","e":"Economics","nav":"1000","category":["1000"]},
{"c":"管理学","e":"Management","nav":"0200","category":["0200","0300","0400","3500"]},
{"c":"文学","e":"Literature","nav":"0700","category":["0700","3900","1200"]},
{"c":"工学","e":"Engineering","nav":"3600","category":["3600","3700","0500","0600","1900","2100","2200","2300","2400","2500","2600","2700","2900","2800","3000","3200","4100","4200"]},
{"c":"法学","e":"Law","nav":"0900","category":["0900"]},
{"c":"历史学","e":"History","nav":"1300","category":["1300"]},
{"c":"理学","e":"Science","nav":"1400","category":["1400","1500","1600","3100","1700","1800","0100","3800","2000"]},
{"c":"教育学","e":"Education","nav":"0800","category":["0800"]},
{"c":"医学","e":"Medicine","nav":"3400","category":["3400","4000"]},
{"c":"农学","e":"Agriculture","nav":"3300","category":["3300"]}
]'
    ];

    /**
     * 特殊字典文件映射关系
     * @var array
     */
    protected $staticFileMap = [
        DictConstant::DD_FUNCTYPE_WITHPARENT => 'functype',
        DictConstant::DD_PROVINCE => 'jobarea',
        DictConstant::DD_CITY => 'jobarea',
        DictConstant::DD_COUNTY => 'jobarea',
        DictConstant::DD_CITY_COUNTY => 'jobarea',
    ];

    public function __construct() {
        $this->dictPath = rtrim(config('app.dict_path'), '/');
        $this->iniPath = $this->dictPath . '/dict_ini';
        $this->jsonPath = $this->dictPath . '/json';
        if (!File::isDirectory($this->iniPath) && !File::makeDirectory($this->iniPath)) {
            throw new RuntimeException('字典INI文件存放目录[' . $this->iniPath . ']生成失败');
        }
    }

    /**
     * 设置多级中间的分隔符
     * @param $delimiter
     */
    public function setDelimiter($delimiter) {
        $this->delimiter = $delimiter;
    }

    /**
     * 生成普通字典INI文件
     * @param $ddType
     * @return bool
     */
    public function generate($ddType) {
        if (in_array($ddType, [DictConstant::DD_PROVINCE, DictConstant::DD_CITY, DictConstant::DD_COUNTY])) {
            return $this->generateArea($ddType);
        } elseif ($ddType === DictConstant::DD_CITY_COUNTY) {
            return $this->generateCityCounty();
        } elseif (in_array($ddType, [DictConstant::DD_FUNCTYPE_WITHPARENT])) {
            return $this->generateWithParent($ddType);
        }

        $content = $this->getStaticContent($ddType);
        $zh = ';' . $ddType . ' 字典' . "\n";
        $en = ';dict for ' . $ddType . "\n";
        foreach ($content as $code => $areaInfo) {
            $zh .= $code . "='" . $this->encodeDictValue($content[$code]['Chinese']) . "'\n";
            $en .= $code . "='" . $this->encodeDictValue($content[$code]['English']) . "'\n";
        }
        $zhFile = $this->iniPath . '/' . $ddType . '_' . DictConstant::LANG_ZH . '.ini';
        $enFile = $this->iniPath . '/' . $ddType . '_' . DictConstant::LANG_EN . '.ini';
        if (false === file_put_contents($zhFile, $zh) || false === file_put_contents($enFile, $en)) {
            throw new RuntimeException('字典INI文件生成失败');
        }
        return true;
    }

    /**
     * 生成json格式字典文件
     * @param $ddType
     * @return bool
     */
    public function generateJson($ddType) {
        switch ($ddType) {
            case DictConstant::DD_CERTIFICATION:
                return $this->generateCertListJson($ddType);
            case DictConstant::DD_INDUSTRY:
            case DictConstant::DD_FUNCTYPE:
            case DictConstant::DD_MAJOR:
                return $this->generateCommonJson($ddType);
            default:
                throw new RuntimeException('json字典类型不支持');
                break;
        }
    }

    /**
     * 通用二级聚合字典json文件
     * @param $ddType
     * @return bool
     */
    protected function generateCommonJson($ddType) {
        $dictContent = $this->formatArrForJson($ddType, true);
        $category = isset($this->categoryListSort[$ddType]) ? json_decode($this->categoryListSort[$ddType], true) : [];
        $dictValue = [];
        if (empty($category)) {
            $dictValue = $dictContent;
        }
        foreach ($category as $item) {
            $dictValueItem = [
                'label' => $item['c'],
                'en' => $item['e'],
                'code' => '',
                'child' => [],
            ];
            $certListSort = $item['category'];
            foreach ($certListSort as $sortCode) {
                if (isset($dictContent[$sortCode])) {
                    $dictValueItem['child'][] = $dictContent[$sortCode];
                }
            }
            $dictValue[] = $dictValueItem;
        }

        $jsonFile = $this->jsonPath . '/' . $ddType . '.json';
        $jsonValue = $this->encodeJsonDictValue($dictValue);
        if (false === file_put_contents($jsonFile, $jsonValue)) {
            throw new RuntimeException('字典JSON文件生成失败');
        }
        return true;
    }

    /**
     * 生成证书字典
     * @param $ddType
     * @return bool
     */
    protected function generateCertListJson($ddType) {
        $dictContent = $this->formatArrForJson($ddType);
        //数组排序
        $certListSort = $this->certListSort;

        $dictValue = [];
        foreach ($certListSort as $sortCode) {
            if (isset($dictContent[$sortCode])) {
                $dictValue[] = $dictContent[$sortCode];
            }
        }
        unset($dictContent);

        $jsonFile = $this->jsonPath . '/' . $ddType . '.json';
        $jsonValue = $this->encodeJsonDictValue($dictValue);
        if (false === file_put_contents($jsonFile, $jsonValue)) {
            throw new RuntimeException('字典JSON文件生成失败');
        }
        return true;
    }

    /**
     * 获取二级菜单
     * @param $ddType
     * @return array
     */
    protected function formatArrForJson($ddType, $withAll = false) {
        //循环便利出二级结构 json 接口
        /*
         [
         {'label' : '语言证书', 'en' : 'Language', 'code' : '0100', 'child' : [
            {'label' : '语言证书', 'en' : 'Language', 'code' : '0100', 'child':[]},
            {'label' : '普通话等级证书', 'en' : 'Mandarin Certificate', 'code' : '0101', 'child':[]},
            {'label' : '托福', 'en' : 'TOEFL', 'code' : '0100', 'child':[]},
         ]'},
        {'label' : '职称证书', 'en' : 'Language', 'code' : '0200', 'child' : [
            {'label' : '初级工程师', 'en' : 'Mandarin Certificate', 'code' : '0101', 'child':[]},
            {'label' : '中级工程师', 'en' : 'TOEFL', 'code' : '0100', 'child':[]},
         ]'},
         ]

        php 数组结构
        [
            ['label' => '语言证书', 'en' => 'Language', 'code' => '0100', 'child' => [
                ['label' => '语言证书1', 'en' => 'Language', 'code' => '0101', 'child' => [],]
                ['label' => '语言证书2', 'en' => 'Language', 'code' => '0102', 'child' => [],]
                ['label' => '语言证书3', 'en' => 'Language', 'code' => '0103', 'child' => [],]
            ]],
            ['label' => '职称证书', 'en' => 'Language', 'code' => '0200', 'child' => [
                ['label' => '语言证书1', 'en' => 'Language', 'code' => '0201', 'child' => [],]
                ['label' => '语言证书2', 'en' => 'Language', 'code' => '0202', 'child' => [],]
                ['label' => '语言证书3', 'en' => 'Language', 'code' => '0203', 'child' => [],]
            ]],
        ]
         */
        $content = $this->getStaticContent($ddType);
        $dictContent = [];
        foreach ($content as $code => $item) {
            if (strlen($code) == 2) {
                $dictContent[$code] = [
                    'label' => $item['Chinese'],
                    'en' => $item['English'],
                    'code' => $code,
                    'child' => [],
                ];
            } else {
                if (substr($code, -2) == '00') {
                    if ($withAll) {
                        $childNode = [
                            $code => [
                                'label' => $item['Chinese'],
                                'en' => $item['English'],
                                'code' => $code,
                                'child' => [],
                            ],
                        ];
                    } else {
                        $childNode = [];
                    }
                    $dictContent[$code] = [
                        'label' => $item['Chinese'],
                        'en' => $item['English'],
                        'code' => $code,
                        'child' => $childNode,
                    ];
                } elseif (isset($dictContent[substr($code, 0, 2) . '00'])) {
                    $dictContent[substr($code, 0, 2) . '00']['child'][$code] = [
                        'label' => $item['Chinese'],
                        'en' => $item['English'],
                        'code' => $code,
                        'child' => [],
                    ];
                }
            }
        }
        return $dictContent;
    }

    /**
     * 生成地区字典
     * @param string $ddType
     * @return bool
     */
    protected function generateArea($ddType) {
        $content = $this->getStaticContent($ddType);
        $zh = ';' . $ddType . ' 字典' . "\n";
        $en = ';dict for ' . $ddType . "\n";
        foreach ($content as $code => $areaInfo) {
            if ($ddType === DictConstant::DD_PROVINCE) {
                $parentCode = substr($code, 0, 2) . '0000';
            } elseif ($ddType === DictConstant::DD_CITY) {
                if (in_array(substr($code, 0, 2) . '0000', $this->municipality)) {
                    $parentCode = substr($code, 0, 2) . '0000';
                } else {
                    $parentCode = substr($code, 0, 4) . '00';
                }

            } else {
                $parentCode = $code;
            }

            $zh .= $code . "='" . $this->encodeDictValue($content[$parentCode]['Chinese']) . "'\n";
            $en .= $code . "='" . $this->encodeDictValue($content[$parentCode]['English']) . "'\n";
        }
        $zhFile = $this->iniPath . '/' . $ddType . '_' . DictConstant::LANG_ZH . '.ini';
        $enFile = $this->iniPath . '/' . $ddType . '_' . DictConstant::LANG_EN . '.ini';
        if (false === file_put_contents($zhFile, $zh) || false === file_put_contents($enFile, $en)) {
            throw new RuntimeException('字典INI文件生成失败');
        }
        return true;
    }

    /**
     * 生成市-区 二级INI字典
     * @return bool
     */
    protected function generateCityCounty() {
        $content = $this->getStaticContent(DictConstant::DD_CITY_COUNTY);
        $zh = ';市区 一级地区字典' . "\n";
        $en = ';dict for city-county' . "\n";
        foreach ($content as $code => $areaInfo) {
            if (substr($code, 2, 4) == '0000') {
                //省一级 或者直辖市一级code，直接返回当前省|直辖市名称
                $value = $content[$code]['Chinese'];
                $eValue = $content[$code]['English'];
            } elseif (!in_array(substr($code, 0, 2) . '0000', $this->municipality) && substr($code, 4, 2) == '00') {
                //非直辖市之外的市一级也直接返回市名称
                $value = $content[$code]['Chinese'];
                $eValue = $content[$code]['English'];
            } else {
                if (in_array(substr($code, 0, 2) . '0000', $this->municipality)) {
                    //直销市下的区
                    $cityCode = substr($code, 0, 2) . '0000';
                    $countyCode = substr($code, 0, 4) . '00';
                } else {
                    //非直辖市下的区
                    $cityCode = substr($code, 0, 4) . '00';
                    $countyCode = $code;
                }
                $value = $content[$cityCode]['Chinese'] . $this->delimiter . $content[$countyCode]['Chinese'];
                $eValue = $content[$cityCode]['English'] . $this->delimiter . $content[$countyCode]['English'];
            }

            $zh .= $code . "='" . $this->encodeDictValue($value) . "'\n";
            $en .= $code . "='" . $this->encodeDictValue($eValue) . "'\n";
        }
        $zhFile = $this->iniPath . '/' . DictConstant::DD_CITY_COUNTY . '_' . DictConstant::LANG_ZH . '.ini';
        $enFile = $this->iniPath . '/' . DictConstant::DD_CITY_COUNTY . '_' . DictConstant::LANG_EN . '.ini';
        if (false === file_put_contents($zhFile, $zh) || false === file_put_contents($enFile, $en)) {
            throw new RuntimeException('字典INI文件生成失败');
        }
        return true;
    }

    /**
     * 生成带父级名称的INI字典
     * @param $ddType
     * @return bool
     */
    protected function generateWithParent($ddType) {
        $content = $this->getStaticContent($ddType);
        $zh = ';' . $ddType . ' 字典' . "\n";
        $en = ';dict for ' . $ddType . "\n";
        foreach ($content as $code => $areaInfo) {
            if (substr($code, 2, 2) == '00') {
                //一级大类
                $value = $content[$code]['Chinese'];
                $eValue = $content[$code]['English'];
            } else {
                $parentCode = substr($code, 0, 2) . '00';
                $value = $content[$parentCode]['Chinese'] . $this->delimiter . $content[$code]['Chinese'];
                $eValue = $content[$parentCode]['English'] . $this->delimiter . $content[$code]['English'];
            }

            $zh .= $code . "='" . $this->encodeDictValue($value) . "'\n";
            $en .= $code . "='" . $this->encodeDictValue($eValue) . "'\n";
        }
        $zhFile = $this->iniPath . '/' . $ddType . '_' . DictConstant::LANG_ZH . '.ini';
        $enFile = $this->iniPath . '/' . $ddType . '_' . DictConstant::LANG_EN . '.ini';
        if (false === file_put_contents($zhFile, $zh) || false === file_put_contents($enFile, $en)) {
            throw new RuntimeException('字典INI文件生成失败');
        }
        return true;
    }

    /**
     * 获取静态字典文件内容
     * @param $ddType
     * @return mixed
     */
    protected function getStaticContent($ddType) {
        if (isset($this->staticFileMap[$ddType])) {
            $staticDict = $this->staticFileMap[$ddType];
        } else {
            $staticDict = $ddType;
        }
        $staticDict = $this->dictPath . '/' . $staticDict . '.php';
        if (!File::isFile($staticDict)) {
            throw new InvalidArgumentException('原始字典静态文件不存在:' . $ddType);
        }
        $content = include $staticDict;
        ksort($content);
        return $content;
    }

    /**
     * 字典内容编码
     * @param $value
     * @return string
     */
    protected function encodeDictValue($value) {
        if ($this->debug) {
            return $value;
        }
        switch ($this->encode) {
            case DictConstant::ENCODE_BASE64:
                return base64_encode($value);
            default;
                return $value;
        }
    }

    /**
     * 将数组转义为json
     * @param $value
     * @return false|string
     */
    protected function encodeJsonDictValue(array $value) {
        //不转义输出
        $option = 0;
        if ($this->debug) {
            //格式化输出
            $option += JSON_PRETTY_PRINT + JSON_UNESCAPED_UNICODE;
        }

        $value = $this->getArrValue($value);

        return json_encode($value, $option);
    }

    /**
     * 移除数组键名
     * @param array $value
     * @return array
     */
    protected function getArrValue(array &$value) {
        $value = array_values($value);
        foreach ($value as &$item) {
            $item['child'] = $this->getArrValue($item['child']);
        }
        return $value;
    }

}